package utils

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"hash/crc32"
	"net"
	"strings"
	"time"
)

type Sender struct {
	conn *net.UDPConn
	app  string
	d1   string
	d2   string
	buf  *bytes.Buffer
}

const (
	headerFlag = "UMS\u0010"
	tailFlag   = "UMS\u0011"
)

func NewSender(app string, umsAgentAddr string) (*Sender, error) {
	raddr, err := net.ResolveUDPAddr("udp", umsAgentAddr)
	if err != nil {
		return nil, err
	}

	conn, err := net.DialUDP("udp", nil, raddr)
	if err != nil {
		return nil, err
	}

	return &Sender{
		conn: conn,
		app:  app,
		d1:   strings.Join([]string{"OWL", "DATA", "0002", app}, `\u0001`) + `\u0001`,
		d2:   `\u0001` + strings.Join([]string{LocalIP(), "Exception", "", "", "ERROR"}, `\u0001`) + `\u0001`,
		buf:  bytes.NewBuffer(make([]byte, 1024)),
	}, nil
}

func (s *Sender) Write(data []byte) (int, error) {
	s.pack(data)

	return s.conn.Write(s.buf.Bytes())
}

func (s *Sender) pack(data []byte) {
	now := time.Now()
	crcnow := crcNow(now)

	s.buf.Reset()

	s.buf.WriteString(headerFlag)

	binary.Write(s.buf, binary.LittleEndian, crcnow)

	s.buf.WriteString(s.d1)

	s.buf.WriteString(now.Format("2006-01-02 15:04:05.000"))

	s.buf.WriteString(s.d2)

	s.buf.Write(data)

	s.buf.WriteString(`\u0004`)

	s.buf.WriteString(tailFlag)

	binary.Write(s.buf, binary.LittleEndian, crcnow)

}

func crcNow(now time.Time) uint32 {
	return crc32.ChecksumIEEE([]byte(fmt.Sprintf("%.9f", now.UnixNano()/1e6)))
}
